package cn.tedu.charging.device.dao.repository.impl;

import cn.tedu.charging.device.dao.repository.StationCacheRepository;
import cn.tedu.charging.device.pojo.StationPO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 分开存
 * 位置信息 存储到 GEO
 * 基本信息 存储到  List Set Zset 能行但是不合理
 */
@Slf4j
@Repository("GEOANDOther")
public class StationCacheGEOANDOtherRepositoryImpl implements StationCacheRepository {

    @Autowired
    private RedisTemplate redisTemplate;


    @Override
    public void save(List<StationPO> stationPOS) {

        //key 站点id value 是位置 站点id和位置的映射
        Map<Integer,Point> map = new HashMap<>();

        //key 站点id value 基本信息 站点id和基本信息的映射
        Map<String,StationPO> stationPOMap = new HashMap<>();

        for (StationPO stationPO : stationPOS) {
            //获取经度
            BigDecimal stationLng = stationPO.getStationLng();
            //获取维度
            BigDecimal stationLat = stationPO.getStationLat();
            //把BigDecimal转换为String 通过方法toEngineeringString
            String engineeringString = stationLng.toEngineeringString();
            //把String转换为Double
            Double stationLngDouble = Double.valueOf(engineeringString);
            //通过经纬度 构建RedisGEO 中的点 Point
            Point point = new Point(stationLngDouble,
                    Double.parseDouble(stationLat.toEngineeringString()));
            //站点id和位置的映射
            map.put(stationPO.getId(),point);

            ListOperations listOperations = redisTemplate.opsForList();
            listOperations.set("station_infos_list",0,stationPO);
            //List
            //---> [stationPO1,stationPO2] ---> 队列  一头放 另外一头取
            //<----[stationPO1,stationPO2]<---- 队列 FIFO
            //栈   一头放 一头取
            //----> [stationPO1,stationPO2]
            //<---- [stationPO1,stationPO2]
            //算法的时间复杂度通常用大O符号表述 时间复杂度
            //O(1) Hash getKey value  hash里存储的对象数量对获取单个对象所消耗的时间影响不大
            //O(N) 链表遍历  数据量越多，查的越慢 链表越长 查的越慢
            //Set 不重复
            SetOperations setOperations = redisTemplate.opsForSet();
            //Zset 不重复 有序 排行榜
            ZSetOperations zSetOperations = redisTemplate.opsForZSet();

        }
        //定义操作geo的 operations 同时绑定key  stations
        BoundGeoOperations stations = redisTemplate.boundGeoOps("stations_geo");
        //批量保存 位置信息 point  和 场站信息 stationPO 到Redis-GEO
        stations.add(map);

        //定义操作String 的 operations
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //批量保存 场站id  和 场站信息 stationPO 到Redis-String
        valueOperations.multiSet(stationPOMap);
    }

    @Override
    public List<StationPO> search(Double longitude, Double latitude, Double distance) {
        //定义操作geo的 operations 同时绑定key  stations
        BoundGeoOperations stations = redisTemplate.boundGeoOps("stations_geo");

        //定义操作String 的 operations
        ValueOperations<String,StationPO> valueOperations = redisTemplate.opsForValue();

        Circle circle  = getCircle(longitude,latitude,distance);
        log.debug("通过RedisGEO Circle:{} 查询附近充电站",circle);
        GeoResults<RedisGeoCommands.GeoLocation<Integer>> radius = stations.radius(circle);
        log.debug("通过RedisGEO Circle:{} 查询附近充电站:{}",circle,radius);

        List<StationPO> stationPOS = new ArrayList<>();

        for (GeoResult<RedisGeoCommands.GeoLocation<Integer>> geoLocationGeoResult : radius) {
            //GeoResults 里的具体内容 用 content表示
            RedisGeoCommands.GeoLocation<Integer> content =
                    geoLocationGeoResult.getContent();
            //name 表示 GEOADD 命令里的 参数  member
            Integer stationId = content.getName();
            //通过 id 去 分开存的 场站信息获取具体场站的数据
            String key = "station_info_string_" + stationId;

        }

        return stationPOS;
    }

    /**
     * 通过点和半径构建一个圆
     * @param longitude
     * @param latitude
     * @param distance
     * @return
     */
    private Circle getCircle(Double longitude, Double latitude, Double distance) {
        //通过入参的经纬度 构建一个点
        Point point = new Point(longitude, latitude);
        //通过入参的距离 设置半径 同时设置单位为 KILOMETERS
        Distance geoDistance = new Distance(distance, Metrics.KILOMETERS);
        //通过点和半径 画圆
        Circle circle = new Circle(point, geoDistance);
        return circle;
    }
}
